package net.ericaro.neojavagengen;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import net.ericaro.neojavagen.Literals;
import net.ericaro.neojavagen.blocks.Block;
import net.ericaro.neojavagen.blocks.BlockStatements;
import net.ericaro.neojavagen.blocks.ExpressionStatement;
import net.ericaro.neojavagen.blocks.ReturnStatement;
import net.ericaro.neojavagen.blocks.StatementExpression;
import net.ericaro.neojavagen.classes.ClassBodyDeclarations;
import net.ericaro.neojavagen.classes.ClassModifier;
import net.ericaro.neojavagen.classes.ConstructorBody;
import net.ericaro.neojavagen.classes.ConstructorDeclaration;
import net.ericaro.neojavagen.classes.ConstructorDeclarator;
import net.ericaro.neojavagen.classes.ConstructorModifier;
import net.ericaro.neojavagen.classes.EnumBody;
import net.ericaro.neojavagen.classes.EnumBodyDeclarations;
import net.ericaro.neojavagen.classes.EnumConstant;
import net.ericaro.neojavagen.classes.EnumConstants;
import net.ericaro.neojavagen.classes.EnumDeclaration;
import net.ericaro.neojavagen.classes.FieldDeclaration;
import net.ericaro.neojavagen.classes.FieldModifier;
import net.ericaro.neojavagen.classes.FormalParameter;
import net.ericaro.neojavagen.classes.FormalParameters;
import net.ericaro.neojavagen.classes.InterfaceTypes;
import net.ericaro.neojavagen.classes.Interfaces;
import net.ericaro.neojavagen.classes.MethodDeclaration;
import net.ericaro.neojavagen.classes.MethodDeclarator;
import net.ericaro.neojavagen.classes.MethodHeader;
import net.ericaro.neojavagen.classes.MethodModifier;
import net.ericaro.neojavagen.classes.ReturnMethodDeclarator;
import net.ericaro.neojavagen.classes.SimpleVariableDeclaratorId;
import net.ericaro.neojavagen.classes.VariableDeclaratorId;
import net.ericaro.neojavagen.classes.VariableDeclarators;
import net.ericaro.neojavagen.expressions.Arguments;
import net.ericaro.neojavagen.expressions.Assignment;
import net.ericaro.neojavagen.expressions.AssignmentOperator;
import net.ericaro.neojavagen.expressions.PrimaryAccess;
import net.ericaro.neojavagen.expressions.This;
import net.ericaro.neojavagen.packages.JavadocComment;
import net.ericaro.neojavagen.packages.TypeDeclaration;
import net.ericaro.neojavagen.types.ClassOrInterfaceType;
import net.ericaro.neojavagen.types.Identifier;
import net.ericaro.neojavagen.types.StringLiteral;

public class EnumPart extends AbstractPart {

	List<String> values = new ArrayList<String>();
	
	public EnumPart(Grammar g, String name) {
		super(g, name);
	}
	
	public EnumPart withLiteral(String value) {
		values.add(value);
		return this;
	}
	
	
	
	public void resolve() {
		// nothing yet
	}

	@Override
	protected TypeDeclaration getTypeDeclaration() {
		EnumConstants enumConstants = new EnumConstants();
		// fill constants
		
		for( String v : values){
			enumConstants.add(createEnumConstant(v));
		}
		
		// workaround, type declaration sadly handles null interfaces, but not empty ones (it generates an "implements" followed by nothing
		InterfaceTypes interfaceTypes = getInterfaceTypes();
		Interfaces interfaces =null ;
		if (interfaceTypes.size() > 0 ) 
			interfaces = new Interfaces(interfaceTypes);

		
		// add body declaration
		ClassBodyDeclarations bodyDeclarations = new ClassBodyDeclarations();
		
		Identifier symbolId = Literals.identifier("symbol");
		
		ClassOrInterfaceType symbolType = Grammar.fromJava(String.class);
		bodyDeclarations.add(
				new FieldDeclaration(EnumSet.noneOf(FieldModifier.class),
						symbolType, 
						new VariableDeclarators(new SimpleVariableDeclaratorId(new VariableDeclaratorId(symbolId)))));
		
		bodyDeclarations.add(
				new ConstructorDeclaration(EnumSet.noneOf(ConstructorModifier.class), new ConstructorDeclarator(null,self.getSimpleTypeName(), new FormalParameters(new FormalParameter(symbolType, new VariableDeclaratorId(symbolId)))), new ConstructorBody(null,new BlockStatements(new ExpressionStatement(createAssignFieldStatement(symbolId)))))
				);
		
		// generate tostring
		BlockStatements blockstatements = new BlockStatements();
		bodyDeclarations.add(
				new MethodDeclaration(new MethodHeader(null,null,EnumSet.of(MethodModifier.PUBLIC),null,new ReturnMethodDeclarator(Grammar.fromJava(String.class), new MethodDeclarator(Literals.identifier("toString")))  ,null ), new Block(blockstatements) )
		);
		blockstatements.add(new ReturnStatement(Grammar.unaryExpression(symbolId)));
		
		
		
		
		
		
		return new EnumDeclaration(null, null, EnumSet.of(ClassModifier.PUBLIC), self.getSimpleTypeName(), interfaces,  new EnumBody(enumConstants, true, new EnumBodyDeclarations(bodyDeclarations)) );
	}

	private JavadocComment generateEnumConstantComment(Identifier nameSymbol, StringLiteral stringLiteral) {

		StringBuilder sb = new StringBuilder();
		
		sb.append( "<i>");
		sb.append(nameSymbol);
		sb.append("</i> &rarr; " );
		sb.append(stringLiteral);
		sb.append("\n *  \n" + 
				" * \n" + 
				" * @author eric@ericaro.net\n" + 
				" *\n" + 
				" ");
		
		return new JavadocComment(Literals.javadocLiteral(sb.toString()));
	}

	private StatementExpression createAssignFieldStatement(Identifier symbolId) {
		// generates a this.symbol = symbol
		return new Assignment(new PrimaryAccess(This.THIS, symbolId), AssignmentOperator.EqualsSymbol, Grammar.unaryExpression(symbolId));
	}

	/** turn a string literal into an enum constant, convert the name, and passes the value as argument
	 * 
	 * @param v
	 * @return
	 */
	private EnumConstant createEnumConstant(String v) {
		final Identifier nameSymbol = Grammar.nameSymbol(v.toUpperCase());
		final StringLiteral stringLiteral = Literals.stringLiteral(v);
		return new EnumConstant(generateEnumConstantComment(nameSymbol,stringLiteral ), null, nameSymbol, new Arguments(Grammar.unaryExpression(stringLiteral)), null);
	}
	

}
